/*
    libhdvbd - Library for building sophisticated DVB applications
    Copyright (C) 2012 Tony Houghton <h@realh.co.uk>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <glib.h>

#include "i18n.h"
#include "section.h"

/* Note const is underused here, because g_list_append objects to it */

static GList *
hdvb_section_list_descriptors(const guint8 *desc_data, int length)
{
    GList *list = NULL;
    int j;
    
    for (j = 0; j < length; )
    {
        HdvbDescriptor *desc = (HdvbDescriptor *) (desc_data + j);
        
        list = g_list_prepend(list, desc);
        j += desc->h.length + 2;
    }
    return list;
}

GList *
hdvb_section_list_pmt_info(const HdvbSection *sec)
{
    GList *list = NULL;
    int i;
    
    for (i = HDVB_SECTION_12BITS(sec, 2) + 4;
            i < HDVB_SECTION_DATA_LENGTH(sec); )
    {
        HdvbPMTStreamInfo *si = (HdvbPMTStreamInfo *) (sec->d + i);
        
        list = g_list_prepend(list, si);
        i += 5 + HDVB_SECTION_PMT_STREAM_INFO_DESCRIPTORS_LENGTH(si);
    }
    return list;
}

GList *
hdvb_section_list_pmt_stream_info_descriptors(const HdvbPMTStreamInfo *si)
{
    return hdvb_section_list_descriptors(si->descriptors,
            HDVB_SECTION_PMT_STREAM_INFO_DESCRIPTORS_LENGTH(si));
}

GList *
hdvb_section_list_sdt_info(const HdvbSection *sec)
{
    GList *list = NULL;
    int i;
    
    for (i = 3; i < HDVB_SECTION_DATA_LENGTH(sec); )
    {
        HdvbSDTInfo *si = (HdvbSDTInfo *) (sec->d + i);
        
        list = g_list_prepend(list, si);
        i += 5 + HDVB_SECTION_SDT_INFO_DESCRIPTORS_LENGTH(si);
    }
    return list;
}

GList *
hdvb_section_list_sdt_info_descriptors(const HdvbSDTInfo *si)
{
    return hdvb_section_list_descriptors(si->descriptors,
            HDVB_SECTION_SDT_INFO_DESCRIPTORS_LENGTH(si));
}

GList *
hdvb_section_list_nit_descriptors(HdvbSection *sec)
{
    return hdvb_section_list_descriptors(sec->d + 2,
            HDVB_SECTION_12BITS(sec, 0));
}

GList *
hdvb_section_list_bouquet_descriptors(const HdvbSection *sec)
{
    return hdvb_section_list_descriptors(sec->d + 2,
            HDVB_SECTION_12BITS(sec, 0));
}

GList *
hdvb_section_list_transport_info(const HdvbSection *sec)
{
    GList *list = NULL;
    int i;
    int j = 4 + HDVB_SECTION_12BITS(sec, 0);
    int ll = HDVB_SECTION_12BITS(sec, j - 2);
    int sl = HDVB_SECTION_DATA_LENGTH(sec);
    
    if (ll + j != sl)
    {
        g_warning("Transport info and total section lengths don't match "
                "(ts %d + desc %d, total %d)", ll, j, sl);
    }
    for (i = j; i - j < ll && i < sl; )
    {
        HdvbTransportInfo *ti = (HdvbTransportInfo *) (sec->d + i);
        
        list = g_list_prepend(list, ti);
        i += 6 + HDVB_SECTION_TRANSPORT_INFO_DESCRIPTORS_LENGTH(ti);
    }
    return list;
}

GList *
hdvb_section_list_transport_info_descriptors(const HdvbTransportInfo *ti)
{
    return hdvb_section_list_descriptors(ti->descriptors,
            HDVB_SECTION_TRANSPORT_INFO_DESCRIPTORS_LENGTH(ti));
}

GList *
hdvb_section_list_eit_info(const HdvbEitSection *sec)
{
    GList *list = NULL;
    int i;
    
    for (i = 6; i < HDVB_SECTION_DATA_LENGTH(sec); )
    {
        HdvbEitInfo *ei = (HdvbEitInfo *) (((HdvbSection *) sec)->d + i);
        
        list = g_list_prepend(list, ei);
        i += 12 + HDVB_EIT_INFO_DESCRIPTORS_LENGTH(ei);
    }
    return list;
}

GList *
hdvb_section_list_eit_info_descriptors(const HdvbEitInfo *info)
{
    return hdvb_section_list_descriptors(info->descriptors,
            HDVB_EIT_INFO_DESCRIPTORS_LENGTH(info));
}

GList *
hdvb_section_list_rst_info(const HdvbRstSection *sec)
{
    GList *list = NULL;
    int i;
    
    for (i = 0; i < HDVB_SECTION_DATA_LENGTH(sec); )
    {
        HdvbRstInfo *ri = (HdvbRstInfo *) (sec->d + i);
        
        list = g_list_prepend(list, ri);
        i += sizeof(HdvbRstInfo);
    }
    return list;
}

GList *
hdvb_target_region_descriptor_list_regions(
        const HdvbTargetRegionDescriptor *desc)
{
    GList *result = NULL;
    int i;
    
    for (i = sizeof(HdvbTargetRegionDescriptor) - HDVB_UNKNOWN_SIZE;
            i < desc->h.length + 2; )
    {
        const guint8 *d = ((const guint8 *) desc) + i;
        guint8 depth = HDVB_REGION_DATA_REGION_DEPTH(d);
        
        result = g_list_prepend(result, (gpointer) d);
        i += 1;
        if (HDVB_REGION_DATA_COUNTRY_CODE_FLAG(d))
            i += 3;
        if (depth == 3)
            i += 4;
        else
            i += depth;
    }
    return result;
}

GList *
hdvb_target_region_name_descriptor_list_regions(
        const HdvbTargetRegionNameDescriptor *desc)
{
    GList *result = NULL;
    int i;
    
    for (i = sizeof(HdvbTargetRegionNameDescriptor) - HDVB_UNKNOWN_SIZE;
            i < desc->h.length + 2; )
    {
        const guint8 *d = ((const guint8 *) desc) + i;
        guint8 depth = HDVB_REGION_NAME_DATA_REGION_DEPTH(d);
        
        result = g_list_prepend(result, (gpointer) d);
        i += 1 + HDVB_REGION_NAME_DATA_NAME_LENGTH(d); /* +1 = depth+length */
        if (depth == 3)
            i += 4;
        else
            i += depth;
    }
    return result;
}

GList *
hdvb_t2_delivery_descriptor_list_simple_frequencies(
        const HdvbT2DeliveryDescriptor *desc)
{
    GList *result = NULL;
    int i;
    
    for (i = 8; i < desc->h.length + 2; i += 6)
    {
        result = g_list_append(result, (gpointer) (desc + i));
    }
    return result;
}
